'''
FileName: MergeCentersBasedOnComponentVectorDissimilarity.py
Author: ChenHongjie
CreatedDate: 2021/01/27
'''
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA

from view.endSrc.UnionFindSet import UnionFindSet

# TODO: This Code is edited from MergeCentersByMultiOb.py, there are some unused variables waiting to be cleared.

class MergeCentersBasedOnComponentVectorDissimilarity:
    '''
        return mergedCentersData
    '''

    def __init__(self, dataset, oblst, isPlot=False):
        self.dataset = dataset
        self.oblst = oblst
        self.componentLabels_ob = np.array([ob.getComponentLabels() for ob in self.oblst]).T
        self.obNum = len(self.oblst)
        self.isPlot = isPlot

        if self.isPlot:
            if dataset.shape[1] > 2:
                pca = PCA(n_components=2)
                self.dataset2d = pca.fit_transform(dataset)
            else:
                self.dataset2d = dataset

    def __MinkowskiDistance(self, p, X, Y=None):
        if Y is None:
            n = X.shape[0]
            D = np.empty((n, n))
            for i in range(n):
                for j in range(n):
                    D[i, j] = np.linalg.norm(X[i] - X[j], ord=p)
            return D
        dis = np.linalg.norm(X - Y, ord=p)
        return dis

    def merge(self, cidlst, centersFromObs, componentCenterNum_ob):
        if self.obNum < 2:
            mergedCentersData = self.dataset[cidlst, :].tolist()
            mergedCentersData = np.unique(mergedCentersData, axis=0).tolist()
            return mergedCentersData

        # plot
        if self.isPlot:
            plt.scatter(self.dataset2d[:, 0], self.dataset2d[:, 1])
            plt.scatter(self.dataset2d[cidlst, 0], self.dataset2d[cidlst, 1], c='r')

        ufs = UnionFindSet(cidlst)
        centersNum = len(cidlst)

        compVec = self.componentLabels_ob[cidlst, :]
        Msimilarity = self.__MinkowskiDistance(p=1, X=compVec)
        Csimilarity = self.__MinkowskiDistance(p=np.inf, X=compVec)

        for i in range(centersNum):
            for j in range(i+1, centersNum):
                id1 = cidlst[i]  # center point id
                id2 = cidlst[j]

                if Msimilarity[i, j] <= self.obNum / 2.0 * 2 and Csimilarity[i, j] < 5:
                    ufs.union(id1, id2)
                    # plot edge
                    if self.isPlot:
                        plt.plot([self.dataset2d[id1, 0], self.dataset2d[id2, 0]], [self.dataset2d[id1, 1], self.dataset2d[id2, 1]], 'k')
        if self.isPlot:
            plt.show()

        merged_cidlst = ufs.getAllHead()
        merged_cidlst.sort()

        mergedCentersData = self.dataset[merged_cidlst, :].tolist()
        mergedCentersData = np.unique(mergedCentersData, axis=0).tolist()
        return mergedCentersData
